﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using GenericGA.extrandom;

namespace GenericGA.generationmaker.selection
{
    /// <summary>
    /// A class which selects individuals by randomly selecting a number of individuals from the population and then returning the best one.
    /// </summary>
    /// <typeparam name="P">The phenotype type, or type of the candidate solutions.</typeparam>
    public class TournamentSelection<P> : Selection<P>
    {

        /// <summary>
        /// A random number generator.
        /// </summary>
        private readonly ExtRandom randGen;

        /// <summary>
        /// The number of individuals to be randomly selected, out of which the best is chosen.
        /// </summary>
        public int TournamentSize
        {
            get
            {
                return tournamentSize;
            }
            set
            {
                if (value < 1)
                {
                    throw new ArgumentException();
                }
                tournamentSize = value;
            }
        }
        private int tournamentSize;

        /// <summary>
        /// The population from which to select.
        /// </summary>
        private List<Individual<P>> population;

        /// <summary>
        /// Create a tournament selecter.
        /// </summary>
        /// <param name="randGen">A random number generator.</param>
        /// <param name="tournamentSize">The number of individual to be randomly selected, out of which the best is chosen.</param>
        public TournamentSelection(ExtRandom randGen, int tournamentSize)
        {
            this.randGen = randGen;
            TournamentSize = tournamentSize;
        }

        /// <summary>
        /// Set the population to select from.
        /// </summary>
        /// <param name="population">The population from which to select.</param>
        /// <param name="fitnessFunction">The fitness function to be used</param>
        public override void InitSelection(List<Individual<P>> population, GA<P>.FitnessFunctionType fitnessFunction)
        {
            this.population = population;
        }

        /// <summary>
        /// Select an individual.
        /// </summary>
        /// <returns>An individual from the population.</returns>
        public override Individual<P> Select()
        {
            int index = randGen.Next(population.Count);
            Individual<P> best = population[index];
            double bestFitness = population[index].CachedFitness;
            for (int i = 0; i < tournamentSize-1; i++)
            {
                index = randGen.Next(population.Count);
                Individual<P> individual = population[index];
                double fitness = population[index].CachedFitness;
                if (fitness > bestFitness)
                {
                    best = individual;
                }
            }
            return best;
        }

    }
}
